package org.gbif.agi.action;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.ServletActionContext;
import org.gbif.agi.model.Genus;
import org.gbif.agi.model.GenusRelationship;
import org.gbif.agi.model.GenusUsage;
import org.gbif.agi.model.dto.GenusDetail;
import org.gbif.agi.model.dto.GenusSearch;
import org.gbif.agi.model.dto.GroupedGenusRelationships;
import org.gbif.agi.service.GenusManager;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.googlecode.jsonplugin.annotations.SMDMethod;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.Preparable;

public class GenusAction implements Preparable {
    private GenusManager manager;
    private List<Genus> genera;
    private List<GenusDetail> genusDetails;
    private Map<String,Object> pagedGenusDetails = new HashMap<String,Object>();
    private Genus genus;
    private Integer id;
    private Integer lft;
    private Integer rgt;
    private Long datasource;
    private Log log = LogFactory.getLog(this.getClass());
    private String q;
    private GroupedGenusRelationships groupedGenusRelationships = new GroupedGenusRelationships();
    
    // ExtJS defines these for paging
    public static final String REQUEST_DIRECTION = "dir";
    public static final String REQUEST_LIMIT = "limit";
    public static final String REQUEST_SORT = "sort";
    public static final String REQUEST_START = "start";
    
    public GenusAction(GenusManager manager) {
        this.manager = manager;
    }

    public String execute() {
        return Action.SUCCESS;
    }

    public String detail() {
        if (id != null) {
            genus = manager.find(id);
	        // build an object that groups the relationships for easy rendering
	        groupRelationships(groupedGenusRelationships, genus.getFromRelationships());
	        groupRelationships(groupedGenusRelationships, genus.getToRelationships());
            return Action.SUCCESS;
        } else {
        	return Action.INPUT;
        }
    }

	/**
	 * Groups the relationships by type
	 */
	private void groupRelationships(GroupedGenusRelationships ggr, List<GenusRelationship> genusRelationships) {
		for (GenusRelationship gr : genusRelationships) {
        	List<GenusRelationship> relationships = ggr.getRelationships().get(gr.getRelationshipType());
        	if (relationships == null) {
        		relationships = new LinkedList<GenusRelationship>();
        		ggr.getRelationships().put(gr.getRelationshipType(), relationships);
        	}
        	relationships.add(gr);
        }
	}
    
	public String browse() {
		log.info("Datasource Id: " + datasource);
		log.info("Id ["+ lft+" - " + rgt + "]");
		genera = manager.find(datasource, lft, rgt);
		populateGenera(genera);
		return Action.SUCCESS;
	}
	
    public String search() {
    	log.info("Search query: " + q);
        if (q!=null && q.length()>0) {
        	genera = manager.find(q);
        	populateGenera(genera);
        }
        return Action.SUCCESS;
    }
	
    // this is used for the AJAX search
    public String searchPage() {
    	log.info(getRequest().getParameter(REQUEST_DIRECTION) + ", " 
    			+ getRequest().getParameter(REQUEST_LIMIT) + ", "
    			+ getRequest().getParameter(REQUEST_SORT) + ", "
    			+getRequest().getParameter(REQUEST_START));
    	
    	boolean asc = true;
    	if ("DESC".equalsIgnoreCase(getRequest().getParameter(REQUEST_DIRECTION))) {
    		asc = false;
    	}
    	int limit = 25;
    	try {
			limit = Integer.parseInt(getRequest().getParameter(REQUEST_LIMIT));
		} catch (NumberFormatException e) {
			log.warn("Paging limit seems corrupt.  Received: " + getRequest().getParameter(REQUEST_LIMIT));
		}
		String sortOn = "genus";
		if (getRequest().getParameter(REQUEST_SORT) != null) {
			sortOn = getRequest().getParameter(REQUEST_SORT);
		}
		int start = 0;
		try {
			start = Integer.parseInt(getRequest().getParameter(REQUEST_START));
		} catch (NumberFormatException e) {
			log.warn("Start seems corrupt.  Received: " + getRequest().getParameter(REQUEST_START));
		}
    	
		genera = manager.find(q, sortOn, asc, start, limit);
		
		// this seems pretty slow!!!
		populateGenera(genera);
		/*
		List<GenusSearch> genusSearches = new LinkedList<GenusSearch>();
		for (Genus g : genera) {
			GenusSearch gs = new GenusSearch();
			genusSearches.add(gs);
			gs.setCanonical(g.getCanonical());
			gs.setAuthorship(g.getAuthorship());			
		}
		*/
		
    	pagedGenusDetails.put("totalCount", manager.count(q));
    	pagedGenusDetails.put("genera", genusDetails);
        return Action.SUCCESS;
    }
    
    protected HttpServletRequest getRequest() {
    	return ServletActionContext.getRequest();
    }
    
	/**
	 * Builds the view specific model
	 */
	private void populateGenera(List<Genus> genera) {
		genusDetails = new LinkedList<GenusDetail>();
		for (Genus g : genera) {
			//log.info("Genus: " + g.getFullScientificName());
			GenusDetail gd = new GenusDetail();
			genusDetails.add(gd);
			gd.setGenus(g);
			
			// look at the usages and see if it in the code
			for (GenusUsage gu : g.getGenusUsages()) {
				gu.getAgiDatasource();
				if (gu.getAgiDatasource().isIcbn()) {
					gd.setIcbn(true);
					if (gu.getRemoteId() == null) {
						gd.setSourceQualifierPercent(50);
					} else {
						gd.setSourceQualifierPercent(100);
					}
				}
				if (gu.getAgiDatasource().isIcnb()) {
					gd.setIcnb(true);
					if (gu.getRemoteId() == null) {
						gd.setSourceQualifierPercent(50);
					} else {
						gd.setSourceQualifierPercent(100);
					}
				}
				if (gu.getAgiDatasource().isIczn()) {
					gd.setIczn(true);
					gd.setSourceQualifierPercent(100);
					if (gu.getRemoteId() == null) {
						gd.setSourceQualifierPercent(50);
					} else {
						gd.setSourceQualifierPercent(100);
					}
				}        			
			}        
			
			// hmmm... nasty nasty ja 
			for (GenusRelationship gr : g.getFromRelationships()) {
				if ("HOMONYM".equalsIgnoreCase(gr.getRelationshipType().getType())) {
					gd.setHomonym(true);
				}        			
			}
			for (GenusRelationship gr : g.getToRelationships()) {
				if ("HOMONYM".equalsIgnoreCase(gr.getRelationshipType().getType())) {
					gd.setHomonym(true);
				}        			
			}
			
			// When we get citations, completeness:
			// 0% - no author or citation
			// 50% - author but no citation
			// 100% - citation and author - TODO citations...
			if (g.getAuthorship() != null) {
				gd.setCompletenessPercent(gd.getCompletenessPercent()+50);
			}
			
			// we don't hold whether it is nomencalturally available yet... so 50% = unknown
			gd.setNomenclatureStandingPercent(50);
			
			
		}
	}

    public String save() {
        manager.save(genus);
        genus = new Genus();
        return execute();
    }

    public String remove() {
        manager.remove(id);
        return execute();
    }

    public void prepare() throws Exception {
        if (id != null)
            genus = manager.find(id);
    }

	/**
	 * @return the manager
	 */
	public GenusManager getManager() {
		return manager;
	}

	/**
	 * @param manager the manager to set
	 */
	public void setManager(GenusManager manager) {
		this.manager = manager;
	}

	/**
	 * @return the genera
	 */
	public List<Genus> getGenera() {
		return genera;
	}

	/**
	 * @param genera the genera to set
	 */
	public void setGenera(List<Genus> genera) {
		this.genera = genera;
	}

	/**
	 * @return the genus
	 */
	public Genus getGenus() {
		return genus;
	}

	/**
	 * @param genus the genus to set
	 */
	public void setGenus(Genus genus) {
		this.genus = genus;
	}

	/**
	 * @return the id
	 */
	public Integer getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(Integer id) {
		this.id = id;
	}

	public String getQ() {
		return q;
	}

	public void setQ(String q) {
		this.q = q;
	}

	public List<GenusDetail> getGenusDetails() {
		return genusDetails;
	}

	public void setGenusDetails(List<GenusDetail> genusDetails) {
		this.genusDetails = genusDetails;
	}

	public GroupedGenusRelationships getGroupedGenusRelationships() {
		return groupedGenusRelationships;
	}

	public void setGroupedGenusRelationships(
			GroupedGenusRelationships groupedGenusRelationships) {
		this.groupedGenusRelationships = groupedGenusRelationships;
	}

	public Integer getLft() {
		return lft;
	}

	public void setLft(Integer lft) {
		this.lft = lft;
	}

	public Integer getRgt() {
		return rgt;
	}

	public void setRgt(Integer rgt) {
		this.rgt = rgt;
	}

	public Long getDatasource() {
		return datasource;
	}

	public void setDatasource(Long datasource) {
		this.datasource = datasource;
	}

	public Map<String, Object> getPagedGenusDetails() {
		return pagedGenusDetails;
	}

	public void setPagedGenusDetails(Map<String, Object> pagedGenusDetails) {
		this.pagedGenusDetails = pagedGenusDetails;
	}

}
